class IUP_TEXT
-- Creates an editable text field.
-- When using UTF-8 strings in GTK be aware that all attributes are indexed by 
-- characters, NOT by byte index, because some characters in UTF-8 can use more 
-- than one byte. This also applies to Windows if FORMATTING=True depending on 
-- the Windows codepage (for example East Asian codepage where some characters 
-- take two bytes).

inherit
	IUP_WIDGET
		undefine
			execute_spin,
			execute_dragbegin,
			execute_dragdatasize,
			execute_dragdata,
			execute_dragend,
			execute_dropdata,
			execute_dropmotion
		redefine
			execute_map,
			execute_unmap,
			execute_destroy,
			execute_getfocus,
			execute_killfocus,
			execute_enterwindow,
			execute_leavewindow,
			execute_k_any,
			execute_help,
			execute_action_fnis,
			execute_button,
			execute_caret,
			execute_dropfiles,
			execute_motion,
			execute_valuechanged,
			execute_maskfail
		end
	IUP_WIDGET_BGCOLOR
	IUP_WIDGET_FGCOLOR
	IUP_WIDGET_SIZE
	IUP_WIDGET_ACTIVE
	IUP_WIDGET_FONT
	IUP_WIDGET_EXPAND
	IUP_WIDGET_SCREENPOSITION
	IUP_WIDGET_POSITION
	IUP_WIDGET_MAXMIN_SIZE
	IUP_WIDGET_TIP
	IUP_WIDGET_RASTERSIZE
	IUP_WIDGET_USERSIZE
	IUP_WIDGET_ZORDER
	IUP_WIDGET_VISIBLE
	IUP_WIDGET_CHILD
	IUP_WIDGET_NAME
	IUP_WIDGET_FOCUS
	IUP_WIDGET_PROPAGATEFOCUS
	IUP_WIDGET_CUSTOM_ATTRIBUTES
	IUP_DRAG_AND_DROP
	IUP_WIDGET_TEXT_COMMON
	IUP_WIDGET_TEXT_SPIN

insert
	IUP_WIDGET_TEXT_CARET
	IUP_WIDGET_TEXT_SELECTION
	IUP_WIDGET_TEXT_SPIN

create {ANY}
	text

feature {ANY}

	text
		-- A new text field.
		local
			a_text, p: POINTER
		do
			a_text := int_text(p)
			set_widget(a_text)

			create_mask_dictionary
		end

	-- Attributes

	set_alignment (value: STRING)
		-- [Windows and GTK Only] (non inheritable): text alignment. Possible 
		-- values: "ALEFT", "ARIGHT", "ACENTER". Default: "ALEFT". In Motif, text 
		-- is always left aligned.
		require
			is_valid: is_valid_alignment(value)
		do
			iup_open.set_attribute(Current, "ALIGNMENT", value)
		end

	set_border (state: BOOLEAN)
		-- (creation only): Shows a border around the text. Default: "True".
		do
			iup_open.set_attribute(Current, "BORDER", boolean_to_yesno(state))
		end

	set_can_focus (state: BOOLEAN)
		-- (creation only) (non inheritable): enables the focus traversal of the 
		-- control. In Windows the control will still get the focus when clicked. 
		-- Default: True.
		do
			iup_open.set_attribute(Current, "CANFOCUS", boolean_to_yesno(state))
		end

	get_count: INTEGER
		-- (read-only): returns the number of characters in the text, including 
		-- the line breaks.
		local
			str: STRING
		do
			str := iup_open.get_attribute(Current, "COUNT")

			if str.is_integer then
				Result := str.to_integer
			end
		end

	set_as_drop_files_target (state: BOOLEAN)
		-- [Windows and GTK Only] (non inheritable): Enable or disable the drop 
		-- of files. Default: False, but if DROPFILES_CB is defined when the 
		-- element is mapped then it will be automatically enabled.
		do
			iup_open.set_attribute(Current, "DROPFILESTARGET", boolean_to_yesno(state))
		end

	set_formatting (state: BOOLEAN)
		-- [Windows and GTK Only] (non inheritable): When enabled allow the use 
		-- of text formatting attributes. In GTK is always enabled, but only when 
		-- MULTILINE=True. Default: False.
		do
			iup_open.set_attribute(Current, "FORMATTING", boolean_to_yesno(state))	
		end

	add_format_tag (name: STRING)
		-- [write only] (non inheritable) Name of a format tag element to be 
		-- added to the IUP_TEXT. The name association must be done before 
		-- setting the attribute.
		do
			iup_open.set_attribute(Current, "ADDFORMATTAG", name)
		end

	add_format_widget (wgt: IUP_FORMATTING)
		-- [write only] (non inheritable) Handle of a format tag element to be 
		-- added to the IUP_TEXT. The tag element will be automatically destroyed 
		-- when the IUP_TEXT is mapped. If the IUP_TEXT is already mapped, the 
		-- format tag is immediately destroyed when the attribute is set. The 
		-- format tag can NOT be reused.
		local
			str: STRING
		do
			create str.from_external(wgt.widget)
			iup_open.set_attribute(Current, "ADDFORMATTAG_HANDLE", str)
		end

	remove_formatting (value: STRING)
		-- Removes the formatting of the current selection if YES, and from all 
		-- text if ALL is used.
		require
			is_valid_remove(value)
		do
			iup_open.set_attribute(Current, "REMOVEFORMATTING", value)
		end

	set_no_hide_sel (state: BOOLEAN)
		-- [Windows Only]: do not hide the selection when the control loses its 
		-- focus. Default: True.
		do
			iup_open.set_attribute(Current, "NOHIDESEL", boolean_to_yesno(state))
		end

	set_overwrite (state: BOOLEAN)
		-- [Windows and GTK Only] (non inheritable): turns the overwrite mode 
		-- ON (True) or OFF (False). Works only when FORMATTING=True.
		local
			str: STRING
		do
			if state then
				str := "ON"
			else
				str := "OFF"
			end
			
			iup_open.set_attribute(Current, "OVERWRITE", str)
		end

	set_password (state: BOOLEAN)
		-- (creation only) [Windows and GTK Only] (non inheritable): Hide the 
		-- typed character using an "*". Default: "False".
		do
			iup_open.set_attribute(Current, "PASSWORD", boolean_to_yesno(state))
		end

	set_value (value: STRING)
		-- (non inheritable): Text entered by the user. The '\n' character 
		-- indicates a new line, valid only when is MULTILINE. After the element 
		-- is mapped and if there is no text will return the empty string "".
		do
			iup_open.set_str_attribute(Current, "VALUE", value)
		end

	get_value: STRING
		-- The value.
		do
			Result := iup_open.get_attribute(Current, "VALUE")
		end

	set_value_masked (value: STRING)
		-- (non inheritable) (write-only): sets VALUE but first checks if it is 
		-- validated by MASK. If not does nothing.
		do
			iup_open.set_attribute(Current, "VALUEMASKED", value)
		end

	set_visible_columns (value: INTEGER)
		-- Defines the number of visible columns for the Natural Size, this means 
		-- that will act also as minimum number of visible columns. It uses a 
		-- wider character size then the one used for the SIZE attribute so 
		-- strings will fit better without the need of extra columns. As for SIZE 
		-- you can set to Void after map to use it as an initial value. Default: 5
		require
			value >= 0
		do
			iup_open.set_attribute(Current, "VISIBLECOLUMNS", value.to_string)
		end
	
	-- Coordination to pos
	convert_xy_to_pos (x, y: INTEGER): INTEGER
		-- Converts a (x,y) coordinate in an string position.
		-- Returns: a position in the string starting at 0. If fails returns -1.
		-- Position is always "column - 1".
		do
			Result := iup_open.iup_convert_xy_to_pos (Current, x, y)	
		end

	
	-- Callbacks

	-- Common
	set_cb_map (act: FUNCTION[TUPLE[IUP_TEXT], STRING])
		-- Called right after an element is mapped and its attributes updated.
		local
			operation: INTEGER
		do
			cb_map := act
			
			if cb_map /= Void then
				operation := 1
			else
				operation := 0
			end
						
			iup_open.set_callback (Current, "MAP_CB", "NONEEDED", operation)
		end

	set_cb_unmap (act: FUNCTION[TUPLE[IUP_TEXT], STRING])
		-- Called right before an element is unmapped.
		local
			operation: INTEGER
		do
			cb_unmap := act

			if cb_unmap /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "UNMAP_CB", "NONEEDED", operation)
		end

	set_cb_destroy (act: FUNCTION[TUPLE[IUP_TEXT], STRING])
		-- Called right before an element is destroyed.
		local
			operation: INTEGER
		do
			cb_destroy := act

			if cb_destroy /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "DESTROY_CB", "NONEEDED", operation)
		end

	set_cb_get_focus (act: FUNCTION[TUPLE[IUP_TEXT], STRING])
		-- Action generated when an element is given keyboard focus. 
		-- This callback is called after the KILLFOCUS_CB of the element 
		-- that loosed the focus. The {IUP}.get_focus function during the 
		-- callback returns the element that loosed the focus.
		local
			operation: INTEGER
		do
			cb_getfocus := act

			if cb_getfocus /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "GETFOCUS_CB", "NONEEDED", operation)
		end

	set_cb_kill_focus (act: FUNCTION[TUPLE[IUP_TEXT], STRING])
		-- Action generated when an element loses keyboard focus. This 
		-- callback is called before the GETFOCUS_CB of the element that 
		-- gets the focus.
		local
			operation: INTEGER
		do
			cb_killfocus := act

			if cb_killfocus /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "KILLFOCUS_CB", "NONEEDED", operation)
		end

	set_cb_enter_window (act: FUNCTION[TUPLE[IUP_TEXT], STRING])
		-- Action generated when the mouse enters the native element. 
		local
			operation: INTEGER
		do
			cb_enterwindow := act

			if cb_enterwindow /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "ENTERWINDOW_CB", "NONEEDED", operation)
		end

	set_cb_leave_window (act: FUNCTION[TUPLE[IUP_TEXT], STRING])
		-- Action generated when the mouse leaves the native element.
		local
			operation: INTEGER
		do
			cb_leavewindow := act

			if cb_leavewindow /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "LEAVEWINDOW_CB", "NONEEDED", operation)
		end

	set_cb_k_any (act: FUNCTION[TUPLE[IUP_TEXT, INTEGER], STRING])
		-- Action generated when a keyboard event occurs.
		-- IUP_WIDGET the element that activated the event.
		-- INTEGER identifier of typed key. Please refer to the Keyboard 
		-- Codes table for a list of possible values.
		--
		-- Returns: If IUP_IGNORE is returned the key is ignored and not
		-- processed by the control and not propagated. If returns 
		-- IUP_CONTINUE, the key will be processed and the event will be
		-- propagated to the parent of the element receiving it, this is 
		-- the default behavior. If returns IUP_DEFAULT the key is processed 
		-- but it is not propagated. IUP_CLOSE will be processed. 
		local
			operation: INTEGER
		do
			cb_k_any := act

			if cb_k_any /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "K_ANY", "NONEEDED", operation)
		end

	set_cb_help (act: PROCEDURE[TUPLE[IUP_TEXT]])
		-- Action generated when the user press F1 at a control. In Motif 
		-- is also activated by the Help button in some workstations 
		-- keyboard.
		-- Returns: IUP_CLOSE will be processed.
		local
			operation: INTEGER
		do
			cb_help := act

			if cb_help /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "HELP_CB", "NONEEDED", operation)
		end

	-- Extra
	set_cb_text (act: FUNCTION[TUPLE[IUP_TEXT, INTEGER, STRING], STRING])
		-- Action generated when the text is edited, but before its value is 
		-- actually changed. Can be generated when using the keyboard, undo 
		-- system or from the clipboard.
		--
		-- ih: identifier of the element that activated the event.
		-- c: valid alpha numeric character or 0.
		-- new_value: Represents the new text value.
		--
		-- Returns: IUP_CLOSE will be processed, but the change will be ignored. 
		-- If IUP_IGNORE, the system will ignore the new value. If c is valid and 
		-- returns a valid alpha numeric-character, this new character will be 
		-- used instead. The VALUE attribute can be changed only if IUP_IGNORE is 
		-- returned.	
		local
			operation: INTEGER
		do
			cb_action := act

			if cb_action /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "ACTION", "Fnis", operation)
		end

	set_cb_button (act: FUNCTION[TUPLE[IUP_TEXT, INTEGER, INTEGER, INTEGER, INTEGER, STRING], STRING])
		-- Action generated when any mouse button is pressed and when it is 
		-- released. Both calls occur before the ACTION callback when button 1 is 
		-- being used.
		-- Use convert_xy_to_pos to convert (x,y) coordinates in character 
		-- positioning.	
		--
		-- IUP_BUTTON: identifies the element that activated the event.
		-- button: identifies the activated mouse button:
		--
		-- 1 - left mouse button (button 1);
		-- 2 - middle mouse button (button 2);
		-- 3 - right mouse button (button 3).
		--
		-- pressed: indicates the state of the button:
		--
		-- 0 - mouse button was released;
		-- 1 - mouse button was pressed.
		-- 
		-- x, y: position in the canvas where the event has occurred, in pixels.
		--
		-- status: status of the mouse buttons and some keyboard keys at the 
		-- moment the event is generated. The following IUP features must be used 
		-- for verification:
		--
		-- is_shift(status)
		-- is_control(status)
		-- is_button_1(status)
		-- is_button_2(status)
		-- is_button_3(status)
		-- is_button_4(status)
		-- is_button_5(status)
		-- is_double(status)
		-- is_alt(status)
		-- is_sys(status)
		--
		-- Returns: IUP_CLOSE will be processed. On some controls if IUP_IGNORE 
		-- is returned the action is ignored (this is system dependent).	
		local
			operation: INTEGER
		do
			cb_button := act

			if cb_button /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "BUTTON_CB", "NONEEDED", operation)
		end

	set_cb_caret (act: FUNCTION[TUPLE[IUP_TEXT, INTEGER, INTEGER, INTEGER], STRING])
		-- Action generated when the caret/cursor position is changed.
		-- 
		-- ih: identifier of the element that activated the event.
		-- lin, col: line and column number (start at 1).
		-- pos: 0 based character position.
		--
		-- For single line controls lin is always 1, and pos is always "col-1".
		local
			operation: INTEGER
		do
			cb_caret := act

			if cb_caret /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "CARET_CB", "NONEEDED", operation)
		end

	set_cb_drop_files (act: FUNCTION[TUPLE[IUP_TEXT, STRING, INTEGER, INTEGER, INTEGER], STRING])
		-- Action called when a file is "dropped" into the control. When several 
		-- files are dropped at once, the callback is called several times, once 
		-- for each file.
		--
		-- If defined after the element is mapped then the attribute 
		-- DROPFILESTARGET must be set to True. [Windows and GTK Only]
		--
		-- ih: identifier of the element that activated the event.
		-- filename: Name of the dropped file.
		-- num: Number index of the dropped file. If several files are dropped, 
		-- num is the index of the dropped file starting from "total-1" to "0".
		-- x: X coordinate of the point where the user released the mouse button.
		-- y: Y coordinate of the point where the user released the mouse button.
		--
		-- Returns: If IUP_IGNORE is returned the callback will NOT be called for 
		-- the next dropped files, and the processing of dropped files will be 
		-- interrupted.	
		local
			operation: INTEGER
		do
			cb_dropfiles := act

			if cb_dropfiles /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "DROPFILES_CB", "NONEEDED", operation)
		end

	set_cb_motion (act: FUNCTION[TUPLE[IUP_TEXT, INTEGER, INTEGER, STRING], STRING])
		-- Action generated when the mouse moves.
		-- Use convert_xy_to_pos to convert (x,y) coordinates in character 
		-- positioning.
		--
		-- ih: identifier of the element that activated the event.
		-- x, y: position in the canvas where the event has occurred, in pixels.
		-- status: status of mouse buttons and certain keyboard keys at the 
		-- moment the event was generated. The same macros used for BUTTON_CB can 
		-- be used for this status.
		local
			operation: INTEGER
		do
			cb_motion := act

			if cb_motion /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "MOTION_CB", "NONEEDED", operation)
		end

	set_cb_value_changed (act: FUNCTION[TUPLE[IUP_TEXT], STRING])
		-- Called after the value was interactively changed by the user.
		local
			operation: INTEGER
		do
			cb_valuechanged := act

			if cb_valuechanged /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "VALUECHANGED_CB", "NONEEDED", operation)
		end

	set_cb_mask_fail (act: FUNCTION[TUPLE[IUP_TEXT, STRING], STRING])
		-- Action generated when the new text fails at the mask check.
		-- ih: identifier of the element that activated the event.
		-- new_value: Represents the new text value.
		local
			operation: INTEGER
		do
			cb_maskfail := act

			if cb_maskfail /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "MASKFAIL_CB", "NONEEDED", operation)
		end


feature {IUP}

	execute_map: STRING
		do
			Result := cb_map.item([Current])
		end

	execute_unmap: STRING
		do
			Result := cb_unmap.item([Current])
		end

	execute_destroy: STRING
		do
			Result := cb_destroy.item([Current])
		end

	execute_getfocus: STRING
		do
			Result := cb_getfocus.item([Current])
		end

	execute_killfocus: STRING
		do
			Result := cb_getfocus.item([Current])
		end

	execute_enterwindow: STRING
		do
			Result := cb_enterwindow.item([Current])
		end

	execute_leavewindow: STRING
		do
			Result := cb_leavewindow.item([Current])
		end

	execute_k_any (c: INTEGER): STRING
		do
			Result := cb_k_any.item([Current, c])
		end

	execute_help
		do
			cb_help.call([Current])
		end

	execute_action_fnis (c: INTEGER; new_value: STRING): STRING
		do
			Result := cb_action.item([Current, c, new_value])
		end

	execute_button (btn, pressed, x, y: INTEGER; status: STRING): STRING
		do
			Result := cb_button.item([Current, btn, pressed, x, y, status])
		end

	execute_caret (lin, col, pos: INTEGER): STRING
		do
			Result := cb_caret.item([Current, lin, col, pos])
		end

	execute_dropfiles (filename: STRING; num: INTEGER; x: INTEGER; y: INTEGER): STRING
		do
			Result := cb_dropfiles.item([Current, filename, num, x, y])
		end

	execute_motion (x, y: INTEGER; status: STRING): STRING
		do
			Result := cb_motion.item([Current, x, y, status])
		end

	execute_valuechanged: STRING
		do
			Result := cb_valuechanged.item([Current])
		end

	execute_maskfail (new_value: STRING): STRING
		do
			Result := cb_maskfail.item([Current, new_value])
		end


feature {}

	-- For callbacks

	cb_map: FUNCTION[TUPLE[IUP_TEXT], STRING]
	cb_unmap: FUNCTION[TUPLE[IUP_TEXT], STRING]
	cb_destroy: FUNCTION[TUPLE[IUP_TEXT], STRING]
	cb_getfocus: FUNCTION[TUPLE[IUP_TEXT], STRING]
	cb_killfocus: FUNCTION[TUPLE[IUP_TEXT], STRING]
	cb_enterwindow: FUNCTION[TUPLE[IUP_TEXT], STRING]
	cb_leavewindow: FUNCTION[TUPLE[IUP_TEXT], STRING]
	cb_k_any: FUNCTION[TUPLE[IUP_TEXT, INTEGER], STRING]
	cb_help: PROCEDURE[TUPLE[IUP_TEXT]]

	cb_action: FUNCTION[TUPLE[IUP_TEXT, INTEGER, STRING], STRING]
	cb_button: FUNCTION[TUPLE[IUP_TEXT, INTEGER, INTEGER, INTEGER, INTEGER, STRING], STRING]
	cb_caret: FUNCTION[TUPLE[IUP_TEXT, INTEGER, INTEGER, INTEGER], STRING]
	cb_dropfiles: FUNCTION[TUPLE[IUP_TEXT, STRING, INTEGER, INTEGER, INTEGER], STRING]
	cb_motion: FUNCTION[TUPLE[IUP_TEXT, INTEGER, INTEGER, STRING], STRING]
	cb_valuechanged: FUNCTION[TUPLE[IUP_TEXT], STRING]

	cb_maskfail: FUNCTION[TUPLE[IUP_TEXT, STRING], STRING]

	-- Internals
	
	int_text (action: POINTER): POINTER
		external "plug_in"
      alias "{
         location: "${sys}/plugins"
         module_name: "iup"
         feature_name: "IupText"
         }"
      end

	-- Validations

	is_valid_alignment (value: STRING): BOOLEAN
		do
			if value.is_equal("ALEFT") or
				value.is_equal("ACENTER") or
				value.is_equal("ARIGHT") then
				Result := True
			else
				Result := False
			end
		end

	is_valid_remove (value: STRING): BOOLEAN
		do
			if value.is_equal("YES") or
				value.is_equal("ALL") then
				Result := True
			else
				Result := False
			end
		end

end

-- The MIT License (MIT)

-- Copyright (c) 2016, 2017, 2018, 2019 by German A. Arias

-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in 
-- all copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE.
